__author__ = 'Muscle'
"""@package Widgets.dialog
The dialog widgets

"""

from mui.muicomponent import MUIComponent, CaptureObject, clamp, _MOUSE_MSGTYPE_

from mui.Widgets.muicanvas import MUICanvas
from mui.Widgets.muiimage import MUIImage
import mui.muiApp as muiApp
import mui.muianim as Anim
from mui.CExt.CW32Window import *
#-----------------------------------------------------------------------
# PopupBase
#-----------------------------------------------------------------------
class PopupBase:
    """ PopupBase is for some controls need to enter it's own message loop
    For examples, dialog, menu ......

    You should override following functions if your control is derived from PopupBase:
        show
        close
        onKey
    """
    def __init__(self, **argd):
        assert isinstance(self, MUIComponent)
        self.breakLoop = True
        self.retValue = False

        # command routing
        self.cmdPath = None

        self.closeDuration = 0

    def show(self):
        """ invoke 'Show Popup' message after the duration of animation
        """
        Anim.schedule_once(self.invoke, self.closeDuration, 'Show Popup')

    def closePopup(self):
        """ close or cancel popup control after duration
        """
        self.invoke('Close Popup')
        self.blind = True
        if self.closeDuration > 0:
            Anim.schedule_once( self.close, self.closeDuration)
        else:
            Anim.postExecute(self.close)

    def onOk(self):
        """ exit popup loop and return 'Ok'
        """
        self.modalRet('Ok')

    def onCancel(self):
        """ exit popup loop and return 'Cancel'
        """
        self.modalRet('Cancel')

    def doBreakLoop(self):
        if not self.breakLoop:
            self.breakLoop = True
            self.visible = False

    def modalRet(self, value = False):
        """
        Don't put value which reference to self, otherwise memory leak due to circular reference.
        """
        self.retValue = value
        self.closePopup()

    def doModal(self):
        assert self.parent and self.window is self.parent and self.breakLoop
        self.retValue = False
        self.breakLoop = False
        self.show()

        window = self.window
        window.hideToolTips()
        # set popup
        popupPool = window._popups
        popupPool.append(self)

        self.setFocus() # we should setFocus after adding into popupPool or it may fail

        muiApp.run_domodal(self)

        # relese popup
        popupPool.remove(self)
        if popupPool:
            for i in popupPool[-1::-1]:
                if not i.dead:
                    i.setFocus()
                    break

        return self.retValue

    def onKey(self, msgType, key, symbol, modifiers):
        if (symbol == VK_ESCAPE or (symbol == VK_F4 and modifiers & MOD_ALT)):
            if not self.breakLoop:
                self.onCancel()
                return True
        return False

class Sizer(MUIImage, CaptureObject):
    """ Sizer is a widget for sizing it's parent widget
    """
    def __init__(self, parent=None, **argd):
        MUIImage.__init__(self, parent, **argd)
        CaptureObject.__init__(self)
        self.blind = False
        self.useGlobalCapture = True
        self.bind('Mouse Enter', self.onChangeCursor, True)
        self.bind('Mouse Leave', self.onChangeCursor, False)

    def onChangeCursor(self, v):
        if not self.window.dragDroping:
            self.window.setCursor('size nwse' if v else 'arrow')


class MUIDialog(MUICanvas, PopupBase):
    """ Dialog
    """
    def __init__(self, parent=None, **argd):
        PopupBase.__init__(self, **argd)
        MUICanvas.__init__(self, parent, **argd)

        self.minSize = 100, 50  # minimal dialog size
        self.visible = False

        # margin
        self.__lm = 0   # left margin
        self.__tm = 0   # top margin
        self.__rm = 0   # right margin
        self.__bm = 0   # bottom margin

        # sizer in right bottom
        self.sizable = False
        self.changeEvent('sizable', self.onSizableChange, postevent = False)

    def passMouseMsg(self, msgType, x, y, **kwargs):
        """
        pass mouse message to children, and then process the mouse message
        @param x: mouse pos x in local coordinate
        @param y: mouse pos y in local coordinate
        @rtype: True if mouse over myself or one of my children
        """

        if msgType == _MOUSE_MSGTYPE_.press and self.hitTest(x, y) and self.visible and not self.blind and self.enabled:
            self.setFocus()

        return super(MUIDialog, self).passMouseMsg(msgType, x, y, **kwargs)

    def show(self):
        super(MUIDialog, self).show()
        PopupBase.show(self)

    def setFocus(self, hint = '', **_argd):
        super(MUIDialog, self).setFocus(hint)
        if self.breakLoop:
            window = self.parent
            if not window:
                return
            if self in window._dialogs:
                window._dialogs.remove(self)
                window._dialogs.append(self)
            window.dirty = True

    def setMargin(self, m):
        s = self.clientSize
        self.__lm, self.__tm, self.__rm, self.__bm = m
        self.clientSize = s

    def getMargin(self):
        return self.__lm, self.__tm, self.__rm, self.__bm
    margin = property(getMargin, setMargin)

    def __setClientSize(self, s):
        self.width = s[0] + self.__lm + self.__rm
        self.height = s[1] + self.__tm + self.__bm

    def __getClientSize(self):
        return self.width - self.__lm - self.__rm, self.height - self.__tm - self.__bm
    clientSize = property(__getClientSize, __setClientSize)

    def getClientPos(self):
        return self.__lm, self.__tm, self.width - self.__rm, self.height - self.__bm
    clientPos = property(getClientPos)

    def setAttachedProperty(self, prop, **argd):
        name = prop['name'].split('.')[-1].lower()
        data = prop['data']
        if name == 'dock' and data.lower() == 'client':
            child = prop['child']
            self.dock(child, {'left':self.__lm, 'top':self.__tm, 'right':self.__rm, 'bottom':self.__bm})
        else:
            super(MUIDialog, self).setAttachedProperty(prop, **argd)

    def onSizableChange(self):
        if self.sizable:
            self.sizer = Sizer(self)
            self.dock(self.sizer, {'right':0, 'bottom':0})
            self.sizer.bind('Capture Begin', self.onSizeStart)
            self.sizer.bind('Capture Offset', self.onSizeMove)
        else:
            if hasattr(self, 'sizer'):
                self.sizer.close()
                self.sizer = None

    def onSizeStart(self, _x, _y):
        self.__capWidth = self.width
        self.__capHeight = self.height

    def onSizeMove(self, x, y):
        self.width = clamp(self.minSize[0], self.__capWidth + x, self.parent.width)
        self.height = clamp(self.minSize[1], self.__capHeight + y, self.parent.height)

    def close(self):
        super(MUIDialog, self).close()
        self.doBreakLoop()

    def onKey(self, msgType, key, symbol, modifiers):
        if PopupBase.onKey(self, msgType, key, symbol, modifiers):
            return True
        return super(MUIDialog, self).onKey(msgType, key, symbol, modifiers)

